package de.sordul.md5crack.impl.optimizedMultiCore;

import java.util.concurrent.Callable;

import de.sordul.md5crack.IMD5Crack;
import de.sordul.md5crack.util.Charset;

/**
 * Multicore implementation of the IMD5Crack interface. Uses CPU to compute String
 * via brute force.
 * 
 * @see de.sordul.md5crack.IMD5Crack
 */
public class MCOptimizedMD5Crack implements IMD5Crack, Callable<String> {

	private static int MAX_STRING_LENGTH = Integer.MAX_VALUE;
	private String candidate;

	private int cores = Runtime.getRuntime().availableProcessors();
	private final Object waitObj = new Object();
	private CrackWorkerManager crackCrackWorkerManager;

	private String md5;
	private Charset charset;
	private int length = 0;

	public String crack(String md5, Charset charset) {
		return crack(md5, charset, MAX_STRING_LENGTH);
	}

	public String crack(String md5, Charset charset, int length) {
		WorkerSettings settings = new WorkerSettings(md5, length, this, charset);
		crackCrackWorkerManager = new CrackWorkerManager(cores, settings);
		crackCrackWorkerManager.startWorkers();
		
		synchronized(waitObj) {
	        while (candidate == null)
				try {
					waitObj.wait();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
	    }	

		crackCrackWorkerManager.stopWorkers();
		return candidate;
	}
	
	@Override
	public String call() throws Exception {
		
		// Return NULL if not all necessary attributes are set
		if(this.md5 == null || this.charset == null) {
			return null;
		}
		
		// Set length to MAX when it's NULL currently
		if(this.length == 0) {
			this.length = MAX_STRING_LENGTH;
		}
		
		// Compute the password and return it
		return crack(md5, charset, length);
	}
	
	public void setCandidate(String candidate) {
		this.candidate = candidate;
	}
	
	public String getCandidate() {
		return this.candidate;
	}
	
	public Object getWaitObj() {
		return waitObj;
	}
	
	public String getMd5() {
		return md5;
	}

	public void setMd5(String md5) {
		this.md5 = md5;
	}

	public Charset getCharset() {
		return charset;
	}

	public void setCharset(Charset charset) {
		this.charset = charset;
	}

	public int getLength() {
		return length;
	}

	public void setLength(int length) {
		this.length = length;
	}

	/**
	 * Stop computation.
	 */
	public void stop() {
		crackCrackWorkerManager.stopWorkers();
	}
}
